﻿<!DOCTYPE html>
<html lang="en">
	<head>
		<title>xg - microsurface</title>

		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<meta name="apple-mobile-web-app-capable" content="yes">
		<meta name="apple-mobile-web-app-status-bar-style" content="black">

		<style>
			body {
				color: #f0f0f0;
				background: #fff;
				padding: 0;
				margin: 0;
				font-weight: bold;
				font-family: Monospace;
				font-size: 13px;
			}

			#info {
				position: absolute;
				top: 0px; width: 100%;
				color: #f0f0f0;
				padding: 5px 0px;
				text-align:center;
				z-index:1000;
			}

			a {
				color: #ff0000;
			}

			#stats { position: absolute; top:10px; left: 10px; white-space: nowrap; }
			#stats #fps { background: transparent !important }
			#stats #fps #fpsText { color: #fff !important }
			#stats #fps #fpsGraph { display: none }
		</style>
	</head>
	<body>

		<div id="container"></div>

		<script src="Scripts/xg.min.js"></script>
		<script src='Scripts/stats.min.js'></script>

		<script type="x-shader/x-vertex" id="vertexShader">

			uniform sampler2D simTexture;

			varying vec3 vViewPosition;

			attribute vec3 rotation;
			attribute vec3 offset;

			vec3 rotateVectorByQuaternion( vec3 v, vec4 q ) {

				return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );

			}

			vec4 axisAngleToQuaternion( vec3 axis, float angle ) {

				vec4 dest = vec4( 0.0 );

				float halfAngle = angle * 0.5;
				float s = sin( halfAngle );

				dest.xyz = axis * s;
				dest.w = cos( halfAngle );

				return dest;

			}

			vec4 multiplyQuaternions( vec4 qa, vec4 qb ) {

				vec4 q = vec4( 0.0 );

				q.x = qa.x * qb.w + qa.w * qb.x + qa.y * qb.z - qa.z * qb.y;
				q.y = qa.y * qb.w + qa.w * qb.y + qa.z * qb.x - qa.x * qb.z;
				q.z = qa.z * qb.w + qa.w * qb.z + qa.x * qb.y - qa.y * qb.x;
				q.w = qa.w * qb.w - qa.x * qb.x - qa.y * qb.y - qa.z * qb.z;

				return q;

			}

			void main() {

				vec4 mvPositionCenter = modelViewMatrix * vec4( offset, 1.0 );
				vec4 projPositionCenter = projectionMatrix * mvPositionCenter;

				vec2 simUV = projPositionCenter.xy * 0.5 + 0.5;
				vec4 simVal = texture2D( simTexture, simUV );

				const float simAmplitude = 10.0;

				vec4 aRotationX = vec4( 1.0, 0.0, 0.0, simAmplitude * simVal.w );
				vec4 aRotationY = vec4( 0.0, 1.0, 0.0, simAmplitude * -simVal.z );

				vec4 qRotationX = axisAngleToQuaternion( aRotationX.xyz, aRotationX.w );
				vec4 qRotationY = axisAngleToQuaternion( aRotationY.xyz, aRotationY.w );

				vec4 qRotation = multiplyQuaternions( qRotationY, qRotationX );

				vec3 newPosition = rotateVectorByQuaternion( position - offset, qRotation ) + offset;

				vec4 mvPosition = modelViewMatrix * vec4( newPosition, 1.0 );
				vViewPosition = mvPosition.xyz;

				gl_Position = projectionMatrix * mvPosition;

			}

		</script>

		<script type="x-shader/x-fragment" id="fragmentShader">

			uniform float amplitude;

			uniform vec3 lPosition1;
			uniform vec3 lPosition2;
			uniform vec3 lPosition3;
			uniform vec3 lPosition4;

			uniform vec3 lightCol1;
			uniform vec3 lightCol2;
			uniform vec3 lightCol3;
			uniform vec3 lightCol4;

			uniform float intensity1;
			uniform float intensity2;
			uniform float intensity3;
			uniform float intensity4;

			varying vec3 vViewPosition;

			float computeAttenuation( float distanceToLight, float lightRange ) {

				const float cutoff = 0.3;
				float denom = distanceToLight / lightRange + 1.0;
				float attenuation = 1.0 / ( denom * denom );
				attenuation = ( attenuation - cutoff ) / ( 1.0 - cutoff );
				attenuation = max( attenuation, 0.0 );
				attenuation *= attenuation;

				return attenuation;

			}

			void main() {

				// point light 1

				const float maxDistance1 = 18000.0;

				vec3 lVector1 = lPosition1 - vViewPosition;
				vec3 lightVec1 = normalize( lVector1 );
				float attentuation1 = computeAttenuation( length( lVector1 ), maxDistance1 );

				// point light 2

				const float maxDistance2 = 18000.0;

				vec3 lVector2 = lPosition2 - vViewPosition;
				vec3 lightVec2 = normalize( lVector2 );
				float attentuation2 = computeAttenuation( length( lVector2 ), maxDistance2 );

				// point light 3

				const float maxDistance3 = 18000.0;

				vec3 lVector3 = lPosition3 - vViewPosition;
				vec3 lightVec3 = normalize( lVector3 );
				float attentuation3 = computeAttenuation( length( lVector3 ), maxDistance3 );

				// point light 4

				const float maxDistance4 = 5000.0;

				vec3 lVector4 = lPosition4 - vViewPosition;
				vec3 lightVec4 = normalize( lVector4 );
				float attentuation4 = computeAttenuation( length( lVector4 ), maxDistance4 );

				// weird syntax to avoid mobile bugs with derivatives
				// - some Android devices can only do per-component derivatives
				// - iOS completely breaks derivatives if there are no uniforms
				//	 involved in further normal use, also computations must be broken
				//	 into pieces, otherwise I guess some optimization goes wild

				vec3 dx = vec3( dFdx( -vViewPosition.x ), dFdx( -vViewPosition.y ), dFdx( -vViewPosition.z ) );
				vec3 dy = vec3( dFdy( -vViewPosition.x ), dFdy( -vViewPosition.y ), dFdy( -vViewPosition.z ) );
				vec3 normal = normalize( cross( dx, dy ) );

				float dotLN1 = dot( normal, lightVec1 );
				dotLN1 = max( dotLN1, 0.0 );
				vec3 diffuse1 = lightCol1 * intensity1 * dotLN1 * attentuation1;

				float dotLN2 = dot( normal, lightVec2 );
				dotLN2 = max( dotLN2, 0.0 );
				vec3 diffuse2 = lightCol2 * intensity2 * dotLN2 * attentuation2;

				float dotLN3 = dot( normal, lightVec3 );
				dotLN3 = max( dotLN3, 0.0 );
				vec3 diffuse3 = lightCol3 * intensity3 * dotLN3 * attentuation3;

				float dotLN4 = dot( normal, lightVec4 );
				dotLN4 = max( dotLN4, 0.0 );
				vec3 diffuse4 = lightCol4 * intensity4 * dotLN4 * attentuation4;

				mgl_FragColor = vec4( diffuse1 + diffuse2 + diffuse3 + diffuse4, 1.0 );

			}

		</script>

		<script>

			var hasWebGL1 = Detector.webgl;
			var hasWebGL2 = Detector.webgl2;

			if ( ! ( hasWebGL1 || hasWebGL2 ) ) Detector.addGetWebGLMessage();

			var backend = hasWebGL2 ? "webgl2" : "webgl1";

			var isOSX = navigator.platform.toLowerCase().indexOf( "mac" ) >= 0;

			//

			var MARGIN = 0;

			var cameraSize = 800;

			var container, stats;
			var camera, scene;
			var renderer, innerRenderer;
			var mesh;

			var lPosition1 = new XG.Vector3();
			var lPosition2 = new XG.Vector3();
			var lPosition3 = new XG.Vector3();
			var lPosition4 = new XG.Vector3();

			var n1 = 0;
			var n2 = 0;
			var n3 = 0;
			var n4 = 0;

			//

			var freezeSimulation = false;

			var mouseX = 0;
			var mouseY = 0;
			var mouse1 = new XG.Vector2();
			var mouse2 = new XG.Vector2();
			var mouseDelta = new XG.Vector2();

			var rtSimulationCurrent;
			var rtSimulationOld;

			var simulationUniforms;
			var simulationMaterial;

			var testMaterial;
			var cubesMaterial;

			var firstFrame = true;

			var strength = 0;
			var strengthDelta = 0;
			var minStrength = 3.0;
			var maxStrength = 6.0;

			var radius = 0.15;

			//

			var clock = new XG.Clock();

			init();
			animate();

			function init() {

				container = document.getElementById( 'container' );

				//

				var r = window.innerWidth / window.innerHeight;

				camera = new XG.OrthographicCamera( -cameraSize * r, cameraSize * r, cameraSize, -cameraSize, 1, 8000 );

				var n = 1500;
				camera.position.z = n;
				camera.position.x = -n;
				camera.position.y = n;

				scene = new XG.Scene();
				camera.lookAt( scene.position );

				// 12 triangles per cube (6 quads)

				var gridSize = 120;
				var cubes = gridSize * gridSize;
				var triangles = 12 * cubes;

				// BufferGeometry with unindexed triangles
				// use vertex colors to store centers of rotations

				var geometry = new XG.Geometry();

				geometry.numVertices = cubes * 8; //  Only 8 vertexes need to be defined per cube
				geometry.numPrimitives = triangles;

				geometry.addIndex( "index", Uint16Array, 3 );
				geometry.addAttribute( "position", Float32Array, 3 );
				geometry.addAttribute( "rotation", Float32Array, 3 );
				geometry.addAttribute( "offset", Float32Array, 3 );

				var positions = geometry.attributes.position.array;
				var rotations = geometry.attributes.rotation.array;
				var offsets = geometry.attributes.offset.array;
				var indices = geometry.attributes.index.array;

				// Generate a single buffer with all the cubes

				var d = 20, d2 = d/2;	// individual triangle size

				//

				var rotation = new XG.Vector3();
				var offset = new XG.Vector3();

				var pA = new XG.Vector3();

				var cb = new XG.Vector3();
				var ab = new XG.Vector3();

				var m = new XG.Matrix4();
				var m2 = new XG.Matrix4();

				var e = new XG.Vector3( 0, 0, 0 );
				var t = new XG.Vector3();
				var tt = new XG.Vector3();
				var rr = new XG.Vector3();
				var u = new XG.Vector3( 0, 1, 0 );

				var v1 = new XG.Vector3( -d2, -d2, -d2 );
				var v2 = new XG.Vector3( d2, -d2, -d2 );
				var v3 = new XG.Vector3( d2, d2, -d2 );
				var v4 = new XG.Vector3( -d2, d2, -d2 );

				var v1b = new XG.Vector3( -d2, -d2, d2 );
				var v2b = new XG.Vector3( d2, -d2, d2 );
				var v3b = new XG.Vector3( d2, d2, d2 );
				var v4b = new XG.Vector3( -d2, d2, d2 );

			    //

				function addVertex( k, x, y, z, va, r ) {

					// positions

					pA.copy( va );

					t.set( x, y, z );
					t.multiplyScalar( 0.5 );

					m2.makeTranslation( t );
					m2.multiplyVector3( pA );

					var j = k * 3;

					positions[ j ] 	   = pA.x;
					positions[ j + 1 ] = pA.y;
					positions[ j + 2 ] = pA.z;

					// offsets

					offsets[ j ] 	 = t.x;
					offsets[ j + 1 ] = t.y;
					offsets[ j + 2 ] = t.z;

					// rotations

					rotations[ j ] 	   = r.x;
					rotations[ j + 1 ] = r.y;
					rotations[ j + 2 ] = r.z;

				}

				var chunkSize = 65536;

				//

				var v = -1; // easier to number vertices from previous example

				for ( var i = 0; i < indices.length; i += 36 ) {

					indices[ i ] 	 = v + 1 % chunkSize;
					indices[ i + 1 ] = v + 2 % chunkSize;
					indices[ i + 2 ] = v + 4 % chunkSize;

					indices[ i + 3 ] = v + 2 % chunkSize;
					indices[ i + 4 ] = v + 3 % chunkSize;
					indices[ i + 5 ] = v + 4 % chunkSize;


					indices[ i + 6 ] = v + 8 % chunkSize;
					indices[ i + 7 ] = v + 6 % chunkSize;
					indices[ i + 8 ] = v + 5 % chunkSize;

					indices[ i + 9 ]  = v + 8 % chunkSize;
					indices[ i + 10 ] = v + 7 % chunkSize;
					indices[ i + 11 ] = v + 6 % chunkSize;

					//

					indices[ i + 12 ] = v + 5 % chunkSize;
					indices[ i + 13 ] = v + 2 % chunkSize;
					indices[ i + 14 ] = v + 1 % chunkSize;

					indices[ i + 15 ] = v + 5 % chunkSize;
					indices[ i + 16 ] = v + 6 % chunkSize;
					indices[ i + 17 ] = v + 2 % chunkSize;


					indices[ i + 18 ] = v + 6 % chunkSize;
					indices[ i + 19 ] = v + 3 % chunkSize;
					indices[ i + 20 ] = v + 2 % chunkSize;

					indices[ i + 21 ] = v + 6 % chunkSize;
					indices[ i + 22 ] = v + 7 % chunkSize;
					indices[ i + 23 ] = v + 3 % chunkSize;

					//

					indices[ i + 24 ] = v + 7 % chunkSize;
					indices[ i + 25 ] = v + 4 % chunkSize;
					indices[ i + 26 ] = v + 3 % chunkSize;

					indices[ i + 27 ] = v + 7 % chunkSize;
					indices[ i + 28 ] = v + 8 % chunkSize;
					indices[ i + 29 ] = v + 4 % chunkSize;


					indices[ i + 30 ] = v + 1 % chunkSize;
					indices[ i + 31 ] = v + 4 % chunkSize;
					indices[ i + 32 ] = v + 5 % chunkSize;

					indices[ i + 33 ] = v + 4 % chunkSize;
					indices[ i + 34 ] = v + 8 % chunkSize;
					indices[ i + 35 ] = v + 5 % chunkSize;

					v += 8;

				}

				//

				var x, y, z;

				var dd = d * 4;
				var s = gridSize * dd;

				x = -0.5 * s;
				y = -0.5 * s;
				z = 0;

				for ( var i = 0; i < geometry.numVertices; i += 8 ) {


					tt.set( Math.random(), Math.random(), Math.random() );
					rr.set( 0.5 * ( i / geometry.numVertices ), 0.5 * ( i / geometry.numVertices ), 0.0 );

					//

					addVertex( i    , x, y, z, v1, rr );
					addVertex( i + 1, x, y, z, v2, rr );
					addVertex( i + 2, x, y, z, v3, rr );
					addVertex( i + 3, x, y, z, v4, rr );

					addVertex( i + 4, x, y, z, v1b, rr );
					addVertex( i + 5, x, y, z, v2b, rr );
					addVertex( i + 6, x, y, z, v3b, rr );
					addVertex( i + 7, x, y, z, v4b, rr );

					//

					x += dd;

					if ( x >= 0.5 * s ) {

						y += dd;
						x = -0.5 * s;

					}

				}

				//

				geometry.offsets = [];

				// break geometry into
			    // chunks
			    // for indices to fit into 16 bit integer number
			    // floor(2^16 / 3) = 21845

				var offsets = geometry.numVertices / chunkSize;

				for ( var i = 0; i < offsets; i ++ ) {

					var offset = {

						start: i * chunkSize * (12 / 8) * 3,
						index: i * chunkSize,
						count: Math.min( triangles - ( i * chunkSize * (12 / 8) ), chunkSize * (12 / 8) ) * 3

					};

					geometry.offsets.push( offset );

				}

				geometry.computeBoundingSphere();

				// Set up custom shader material

				uniforms = {

					"amplitude"	: { type: "f", value: 8.0 },
					"simTexture": { type: "t", value: null },

					"intensity1": { type: "f", value: 1.0 },
					"intensity2": { type: "f", value: 1.0 },
					"intensity3": { type: "f", value: 1.0 },
					"intensity4": { type: "f", value: 1.0 },

					"lPosition1": { type: "v3", value: new XG.Vector3() },
					"lPosition2": { type: "v3", value: new XG.Vector3() },
					"lPosition3": { type: "v3", value: new XG.Vector3() },
					"lPosition4": { type: "v3", value: new XG.Vector3() },

					"lightCol1": { type: "c", value: new XG.Color() },
					"lightCol2": { type: "c", value: new XG.Color() },
					"lightCol3": { type: "c", value: new XG.Color() },
					"lightCol4": { type: "c", value: new XG.Color() },

				};

				var extensions = { "OES_standard_derivatives": true };

				cubesMaterial = new XG.ShaderMaterial( {

					uniforms		: uniforms,
					vertexShader	: document.getElementById( 'vertexShader' ).textContent,
					fragmentShader	: document.getElementById( 'fragmentShader' ).textContent,
					extensions		: extensions

				});

				//

				mesh = new XG.Mesh( geometry, cubesMaterial );
				scene.add( mesh );

				//

				var ULTRA_THRESHOLD = 3000;

				var gpuDetector = new GPUDetector();
				var gpuData = gpuDetector.detectGPU();

				var pars = { antialias: true, clearColor: 0x000000, clearAlpha: 0, alpha: false, backend: backend };

				if ( Detector.isMobile ) {

					pars.antialias = false;
					pars.devicePixelRatio = 1.0;

				}

				if ( ( gpuData && gpuData.rawScore < ULTRA_THRESHOLD ) || isOSX ) {

					pars.devicePixelRatio = 1.0;

				}

				renderer = new XG.ForwardRenderer( pars );
				renderer.setSize( window.innerWidth, window.innerHeight );

				renderer.domElement.style.position = "absolute";
				renderer.domElement.style.top = MARGIN + "px";
				container.appendChild( renderer.domElement );

				innerRenderer = renderer instanceof XG.DeferredRenderer ? renderer.renderer : renderer;

				//

				stats = new Stats();
				stats.domElement.style.position = 'absolute';
				stats.domElement.style.top = '10px';
				container.appendChild( stats.domElement );

				//

				window.addEventListener( 'resize', onWindowResize, false );
				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
				document.addEventListener( 'mousedown', onDocumentMouseDown, false );
				document.addEventListener( 'mouseup', onDocumentMouseUp, false );
				renderer.domElement.addEventListener( 'touchmove', onTouchMove, false );

				// compatibility checks & fallbacks

				var bufferPars = {};
				detectSupport( bufferPars );

				// init dynamic textures

				setupSimulation( bufferPars );

			}

			// --------------------------------------------------------------------------------------------------------------------

			function onDocumentMouseDown( event ) {

				strengthDelta = 15.5;
				radius = 0.05;

				document.body.style.cursor = "pointer";

			}

			function onDocumentMouseUp( event ) {

				strengthDelta = -5.5;
				radius = 0.15;

				document.body.style.cursor = "default";

			}

			function onDocumentMouseMove( event ) {

				mouseX = event.clientX / window.innerWidth;
				mouseY = 1.0 - event.clientY / window.innerHeight;

			}

			function onTouchMove( event ) {

				event.preventDefault();

				var touches = event.touches;
				var touch = touches[ 0 ];

				mouseX = touch.clientX / window.innerWidth ;
				mouseY = 1.0 - ( touch.clientY / window.innerHeight );

			}

			// --------------------------------------------------------------------------------------------------------------------

			function detectSupport( parameters ) {

				var minFilter;
				var magFilter;
				var bufferType;
				var internalFormat;

				if ( innerRenderer.supportsRGBAHalfFloatRenderTarget() ) {

					bufferType 	   = ( backend === "webgl2" ) ? XG.HalfFloatType2 : XG.HalfFloatType1;
					internalFormat = ( backend === "webgl2" ) ? XG.RGBA16F : XG.RGBAFormat;

					if ( innerRenderer.supportsHalfFloatTexturesLinear() ) {

						magFilter = XG.LinearFilter;
						minFilter = XG.LinearFilter;

					} else {

						magFilter = XG.NearestFilter;
						minFilter = XG.NearestFilter;

					}

				} else if ( innerRenderer.supportsRGBAFloatRenderTarget() ) {

					bufferType = XG.FloatType;
					internalFormat = ( backend === "webgl2" ) ? XG.RGBA32F : XG.RGBAFormat;

					if ( innerRenderer.supportsFloatTexturesLinear() ) {

						magFilter = XG.LinearFilter;
						minFilter = XG.LinearFilter;

					} else {

						magFilter = XG.NearestFilter;
						minFilter = XG.NearestFilter;

					}

				} else {

					bufferType = XG.UnsignedByteType;

					magFilter = XG.LinearFilter;
					minFilter = XG.LinearFilter;

				}

				parameters.minFilter = minFilter;
				parameters.magFilter = magFilter;
				parameters.bufferType = bufferType;
				parameters.internalFormat = internalFormat;

			}

			// --------------------------------------------------------------------------------------------------------------------

			function setupSimulation( parameters ) {

				var rtWidth  = window.innerWidth;
				var rtHeight = window.innerHeight;

				if ( Detector.isMobile ) {

					rtWidth  = window.innerWidth * 0.5;
					rtHeight = window.innerHeight * 0.5;

				}

				var rtParams = {

					"minFilter": parameters.minFilter,
					"magFilter": parameters.magFilter,
					"wrapS": XG.RepeatWrapping,
					"wrapT": XG.RepeatWrapping,
					"stencilBuffer": false,
					"depthBuffer": false,
					"format"		: XG.RGBAFormat,
					"internalFormat": parameters.internalFormat,
					"type"			: parameters.bufferType

				};

				rtSimulationCurrent = new XG.RenderTarget( rtWidth, rtHeight, rtParams );
				rtSimulationCurrent.generateMipmaps = false;

				rtSimulationOld = rtSimulationCurrent.clone();

				//

				var shader = XG.CubeSimulationShader;

				simulationMaterial = new XG.ShaderMaterial( {

					"fragmentShader": shader.fragmentShader,
					"vertexShader"	: shader.vertexShader,
					"uniforms"		: XG.UniformsUtils.clone( shader.uniforms )

				} );

				simulationUniforms = simulationMaterial.uniforms;

				simulationUniforms[ "tOldSim" ].value = rtSimulationOld;
				simulationUniforms[ "resolution" ].value.set( rtWidth, rtHeight );

				//

				testMaterial = new XG.EmissiveMaterial( { color: 0xffffff } );
				testMaterial.map = rtSimulationCurrent;

			}

			// --------------------------------------------------------------------------------------------------------------------

			function onWindowResize() {

				var r = window.innerWidth / window.innerHeight;

				camera.left   = -cameraSize * r;
				camera.right  =  cameraSize * r;
				camera.top    =  cameraSize;
				camera.bottom = -cameraSize;

				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

				//

				var rtWidth  = window.innerWidth;
				var rtHeight = window.innerHeight;

				simulationUniforms[ "resolution" ].value.set( rtWidth, rtHeight );

			}

			//

			function animate() {

				requestAnimationFrame( animate );

				render();
				stats.update();

			}

			function render() {

				var delta = clock.getDelta();
				var time = clock.elapsedTime * 1;

				uniforms[ "amplitude" ].value += 4 * delta;

				// update mouse

				strength = XG.Math.clamp( strength + strengthDelta * delta, minStrength, maxStrength );

				mouse1.copy( mouse2 );
				mouse2.set( mouseX, mouseY );

				mouseDelta.sub( mouse2, mouse1 );
				mouseDelta.multiplyScalar( strength );


				// update simulation

				if ( ! freezeSimulation ) {

					renderer.autoClear = false;

					simulationUniforms[ "tOldSim" ].value = rtSimulationOld;
					simulationUniforms[ "time" ].value = time;
					simulationUniforms[ "timeDelta" ].value = delta * 100;
					simulationUniforms[ "mouse" ].value.copy( mouse2 );
					simulationUniforms[ "mouseDelta" ].value.copy( mouseDelta );
					simulationUniforms[ "radius" ].value = radius;

					//console.log( mouse.x, mouse.y );
					//console.log( mouseDelta.x, mouseDelta.y );

					// update simulation

					var screenScene = XG.EffectComposer.scene;
					var screenCamera = XG.EffectComposer.camera;
					var screenMaterials = XG.EffectComposer.quad.materials;

					if ( firstFrame ) {

						//innerRenderer.setClearColorHex( 0x000000, 0 );
						//innerRenderer.clearTarget( rtSimulationCurrent );
						firstFrame = false;

					}

					screenMaterials[ 0 ] = simulationMaterial;
					innerRenderer.render( screenScene, screenCamera, rtSimulationCurrent, false );

					var tmp = rtSimulationCurrent;
					rtSimulationCurrent = rtSimulationOld;
					rtSimulationOld = tmp;

					renderer.autoClear = true;

					if ( 0 ) {

						testMaterial.map = rtSimulationCurrent;

						screenMaterials[ 0 ] = testMaterial;
						innerRenderer.render( screenScene, screenCamera );

					}

					cubesMaterial.uniforms[ "simTexture" ].value = rtSimulationCurrent;

					// update lights

					var amplitude = uniforms[ "amplitude" ].value;

					n1 = Math.sin( amplitude * 0.01 ) * 2000.0;
					lPosition1.set( -n1, n1, 0.0 );

					n2 = Math.sin( amplitude * 0.02 ) * 1000.0;
					lPosition2.set( n2, 0.0, -n2 * 0.5 );

					n3 = Math.sin( amplitude * 0.03 ) * 1000.0;
					lPosition3.set( -n3, 0.0, n3 * 0.5 );

					lPosition4.set( -2000.0, 2000.0, 0.0 );

					camera.matrixWorldInverse.multiplyVector3( lPosition1 );
					camera.matrixWorldInverse.multiplyVector3( lPosition2 );
					camera.matrixWorldInverse.multiplyVector3( lPosition3 );
					camera.matrixWorldInverse.multiplyVector3( lPosition4 );

					cubesMaterial.uniforms[ "lPosition1" ].value.copy( lPosition1 );
					cubesMaterial.uniforms[ "lPosition2" ].value.copy( lPosition2 );
					cubesMaterial.uniforms[ "lPosition3" ].value.copy( lPosition3 );
					cubesMaterial.uniforms[ "lPosition4" ].value.copy( lPosition4 );

					var intensity1 = 0.75 + ( Math.sin( amplitude * 0.2 )  + 1.0 ) * 0.5;
					var intensity2 = 0.75 + ( Math.sin( amplitude * 0.05 ) + 1.0 ) * 0.5;
					var intensity3 = 0.75 + ( Math.sin( amplitude * 0.1 )  + 1.0 ) * 0.5;
					var intensity4 = 1;

					cubesMaterial.uniforms[ "intensity1" ].value = intensity1;
					cubesMaterial.uniforms[ "intensity2" ].value = intensity2;
					cubesMaterial.uniforms[ "intensity3" ].value = intensity3;
					cubesMaterial.uniforms[ "intensity4" ].value = intensity4;

					cubesMaterial.uniforms[ "lightCol1" ].value.setRGB( 0.25, 0.0, 0.52 );
					cubesMaterial.uniforms[ "lightCol2" ].value.setRGB( 1.0, 0.25, 0.0 );
					cubesMaterial.uniforms[ "lightCol3" ].value.setRGB( 0.0, 0.5, 1.0 );
					cubesMaterial.uniforms[ "lightCol4" ].value.setRGB( 0.5, 0.0, 1.0 );

				}

				renderer.render( scene, camera );

			}

		</script>

	</body>
</html>
